
#ifndef __M_DB_H__
#define __M_DB_H__

#include "util.hpp"
#include <mutex>
#include <cassert>

// 对用户表的管理
// mysql提供的sql执行接口是线程安全的
// 但不同的接口间不是线程安全的
class user_table
{
public:
    user_table(const std::string &host,
               const std::string &username,
               const std::string &passwd,
               const std::string &dbname,
               uint16_t port = 3306)
    {
        _mysql = mysql_util::mysql_create(host, username, passwd, dbname, port);
        assert(_mysql != nullptr);
    }
    ~user_table()
    {
        mysql_util::mysql_destory(_mysql);
        _mysql = nullptr;
    }

    // 注册时新增用户
    bool insert(const Json::Value &user)
    {
        Json::Value usr;
        if (user["username"].isNull() || user["password"].isNull())
        {
            DLOG("username and password is null!!!");
            return false;
        }

        if (select_by_name(user["username"].asCString(), usr) == true)
        {
            DLOG("用户信息已经存在:%s ", user["username"].asCString());
            return false;
        }

#define INSERT_SQL "insert into user values(null,'%s',password('%s'),1000,0,0);"
        char sql[4096] = {0};
        sprintf(sql, INSERT_SQL, user["username"].asCString(), user["password"].asCString());
        bool ret = mysql_util::mysql_exec(_mysql, sql);
        if (ret == false)
        {
            ELOG("新增用户失败:%s", user["username"].asCString());
            return false;
        }
        return true;
    }
    // 用户登录验证，并获取用户信息
    bool login(Json::Value &user)
    {
#define LOGIN_SQL "select id,score,total_count,win_count from user where username='%s' and password=password('%s');"
        char sql[4096] = {0};
        sprintf(sql, LOGIN_SQL, user["username"].asCString(), user["password"].asCString());
        MYSQL_RES *res;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            bool ret = mysql_util::mysql_exec(_mysql, sql);
            if (ret == false)
            {
                ELOG("login failed");
                return false;
            }
            // 获取用户信息
            res = mysql_store_result(_mysql);
            if (res == nullptr)
            {
                DLOG("get res failed");
                return false;
            }
        }

        MYSQL_ROW row = mysql_fetch_row(res);
        if (row == nullptr)
        {
            return false;
        }
        user["id"] = (Json::UInt64)std::stol(row[0]);
        user["score"] = (Json::UInt64)std::stol(row[1]);
        user["total_count"] = std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);
        mysql_free_result(res);

        return true;
    }
    // 通过用户名获取信息
    bool select_by_name(const std::string &name, Json::Value &user)
    {
#define SELECT_BY_NAME_SQL "select id,score,total_count,win_count from user where username='%s';"
        char sql[4096] = {0};
        sprintf(sql, SELECT_BY_NAME_SQL, static_cast<const char *>(name.c_str()));
        MYSQL_RES *res;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            bool ret = mysql_util::mysql_exec(_mysql, sql);
            if (ret == false)
            {
                // ELOG("the user information is not exists!!!");
                return false;
            }
            // 获取用户信息
            res = mysql_store_result(_mysql);
            if (res == nullptr)
            {
                DLOG("get res failed");
                return false;
            }
        }

        MYSQL_ROW row = mysql_fetch_row(res);
        // bug:未判断row是否为空，导致程序崩溃（触发段错误）
        if (row == nullptr)
        {
            return false;
        }
        user["username"] = name;
        user["id"] = (Json::UInt64)std::stol(row[0]);
        user["score"] = (Json::UInt64)std::stol(row[1]);
        user["total_count"] = std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);
        mysql_free_result(res);

        return true;
    }
    // 通过用户id获取用户信息
    bool select_by_id(uint64_t id, Json::Value &user)
    {
#define SELECT_BY_ID_SQL "select username,score,total_count,win_count from user where id='%ld';"
        char sql[4096] = {0};
        sprintf(sql, SELECT_BY_ID_SQL, id);
        MYSQL_RES *res;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            bool ret = mysql_util::mysql_exec(_mysql, sql);
            if (ret == false)
            {
                ELOG("the user information is not exists!!!");
                return false;
            }
            // 获取用户信息
            res = mysql_store_result(_mysql);
            if (res == nullptr)
            {
                DLOG("get res failed");
                return false;
            }
        }

        MYSQL_ROW row = mysql_fetch_row(res);
        if (row == nullptr)
        {
            return false;
        }
        user["username"] = row[0];
        user["score"] = (Json::UInt64)std::stol(row[1]);
        user["total_count"] = std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);
        mysql_free_result(res);

        return true;
    }
    // 用户胜利时，修改用户的信息,分数增加30分
    bool win(uint64_t id)
    {
#define USER_WIN "update user set score=score+30,win_count=win_count+1,total_count=total_count+1 where id='%ld';"
        char sql[4096];
        sprintf(sql, USER_WIN, id);
        bool ret = mysql_util::mysql_exec(_mysql, sql);
        if (ret == false)
        {
            ELOG("update information for win failed!!!");
            return false;
        }
        return true;
    }
    // 用户失败时，修改用户的信息
    bool lose(uint64_t id)
    {
#define USER_LOSE "update user set score=score-30,total_count=total_count+1 where id='%ld';"
        char sql[4096];
        sprintf(sql, USER_LOSE, id);
        bool ret = mysql_util::mysql_exec(_mysql, sql);
        if (ret == false)
        {
            ELOG("update information for lose failed!!!");
            return false;
        }
        return true;
    }

private:
    MYSQL *_mysql;
    std::mutex _mutex;
};

#endif